/*-----------schema.c------------*/

/*Read a file from the standard input.
*Write one of the three schemas files to the standard output
*depending on the runtime switch.
*if -1 is on the command line,
* 	 the #define statements are built.
*if -2 is on the command line,
*  	the file of the ascii strings for
*	files and data element names is built.
*if -3 is on the command line,
*the database schema array source file is built.
*/


#include<stdio.h>
#define SCHEMA
#include "cdata.h"


#define MXCAT 3 /*maximum elements concatenated per index*/
#define NAMLEN 31




struct dict {             		/*data element dictionary*/

		char dename [NAMLEN+1];	/* name 		 */
		char detype;		/* type 		*/
		int delen;		/* length		*/
		char *demask;		/* display mask		*/

		 }dc[MXELE];



int dectr = 0;  		/*data elements dictionary*/
int fctr = 0; 			/*files in database*/
char filename [MXFILS] [NAMLEN+1];		/*file name strings*/
char fileele [MXFILS] [MXELE];		/*elements in files */
int ndxele [MXFILS] [MXINDEX] [MXCAT]; 	/* indices */



char word[NAMLEN+1];
char *malloc();
int lnctr = 0; 		 /* input stream line counter */
char ln [160];




/*---------------------error messages---------------------*/

char *ers[] = { "invalid name",        		/* 1 */
		"invalid length",		/* 2 */
		"comma missing",		/* 3 */	
                "invalid length",		/* 4 */
		"quote missing",		/* 5 */
		"schema missing",		/* 6 */
		"<command> missing",		/* 7 */
		"unexpected end of line",	/* 8 */
		"duplicate file name",		/* 9 */
		"unknown data element",		/* 10 */
		"too many data elements",	/* 11 */
		"out of memory",		/* 12 */
		"unknown file name",		/* 13 */
		"too many indeces in file",	/* 14 */
		"too many elements in index",	/* 15 */
		"duplicate data elements",	/* 16 */
		"too many files",		/* 17 */
		"invalid cmmand line switch",	/* 18 */
		
               };




void de_dict(), files(), keys(), schout(), defout(), lcase(), strout(),
     error(), get_line(), skip_white(), name_val(), numb_val(), expect_comma(),
     depart();



char *get_word();
#define iswhite(c) ((c)==' '||(c)=='\t')
#define REMARK  ';'



/* --------------------------main program---------------------*/

main(argc, argv)
int argc;
char *argv[];


{
		get_line();
		if(strncmp(ln,"#schema ",8))
	   error(6);
		else {
	   get_word(ln+8);
	   name_val();
	   printf("\n/");
		   printf("*-----------------%s------------*/\n",word);
}

get_line();
while(strncmp(ln,"#end schema",11))
{
		if(strncmp(ln,"#dictionary",11) == 0)
		de_dict();
		else if(strncmp(ln,"#file ",6) == 0)
		files();
		else if(strncmp(ln,"#key ",5) == 0)
		keys();
		else
		error(7);
		get_line();
}
		if(argc > 1) {
			if(strcmp(argv[1], "-1") == 0)
			defout();
			else if(strcmp(argv[1], "-2") == 0)
			strout();
			if(strcmp(argv[1], "-3") == 0)
			schout();
			else
			error(18);
}
		else
		error(18);
		depart(0);
}



/*--------build the data element dictionary--------*/
static void de_dict()
{
		char *cp, *cp1;
		int el;
		while(TRUE) {
			get_line();
			if(strncmp(ln,"#end dictionary",15) == 0)
			break;
			if(dectr == MXELE) {
			error(11);
			continue;
            }



		cp = get_word(ln);
		name_val();
		for(el = 0; el < dectr;el++)
		if(strncmp(word,dc[el].dename) == 0) {
		error(16);
		continue;
           }
		strcpy(dc[dectr].dename,word);
		expect_comma(&cp);
		skip_white(&cp);
	switch (*cp)     {
			case 'A':
			case 'Z':
			case 'C':
			case 'N':
			case 'D':           break;
			default :           error(4);
			 continue;
}
    dc[dectr].detype = *cp++;
	expect_comma(&cp);
	cp = get_word(cp);
	numb_val();
	dc[dectr].delen = atoi(word);
	expect_comma(&cp);
	skip_white(&cp);
		if ( *cp != '"')   {
			error(5);
			continue;
        }
		cp1 = cp + 1;
		while (*cp1 != '"' && *cp1 && *cp1 != '\n')
            cp1++;
            if( *cp1++ != '"') {
			error(5);
			continue;
}
		*cp1 = '\0';
		if((dc[dectr].demask = malloc((cp1-cp)+1)) == 0) {
			error(12);
			depart(1);

}
		strcpy(dc[dectr].demask, cp);
		dectr++;
}

}
   
		
/*------------build the file definitions------------*/

static void files()
{

			int i,el = 0;
			if (fctr == MXFILS)
			error(17);
			get_word(ln+6);       /*get the file name*/
			name_val();               /*validate it*/
		for(i=0 ; i< fctr; i++) /* already assigned ?     */
			if (strcmp(word, filename[i]) == 0)
			error(9);

		strcpy(filename[fctr],word);

/*-----------------------process the file data elements--------*/
while (TRUE)   {

		get_line();
		if(strncmp(ln, "#end file", 9) == 0)
		break;
		if(el == MXELE)     {
			error(11);
			continue;
	}

	get_word(ln);                      /* get a data element*/
	for (i=0; i<dectr;i++)            /* in dictionary?     */
	if (strcmp(word,dc[i].dename) == 0)
		break;
		if (i == dectr)
		error(10);
	else if (fctr < MXFILS)
		fileele [fctr] [el++] = i + 1;  /*post to file */

}
		if (fctr < MXFILS)
		fctr++;
}

/*-----------------build the index descriptions---------*/
static void keys()
{
		char *cp;
		int f,el,x,cat = 0;
		cp = get_word(ln + 5);         /* get the file name*/
		for (f=0;f<fctr;f++)                /* in the schema?*/
		if (strcmp(word, filename[f]) == 0)
		break;
		if (f == fctr) {
		error(13);
		return;
}
for(x = 0;x < MXINDEX;x++)
		if (*ndxele [f] [x] == 0)
		break;
		if (x == MXINDEX) {
		error(14);
		return;
}
while( cat < MXCAT)  {
		cp = get_word(cp);          /* get the index name*/
		for(el = 0;el < dectr;el++)/* in dictionary?*/
		if(strcmp(word,dc[el].dename) == 0)
		break;
		if(el == dectr) {
		error(10);
		break;
}

ndxele[f][x][cat++] = el+1;  /*post element*/
skip_white(&cp);
if (*cp++ != ',')         /* concatenated index?*/
		break;
		if(cat == MXCAT)   {
		error(15);
		break;
}
}
}
/*-------------write the schema source language--------*/

static void schout()
{

		int f,el, x , x1, cat , fel;
		char name [NAMLEN+1];


/*------------data element lengths--------*/
		printf("\n\nint ellen [] = {");
		for (el = 0;el < dectr; el++) {
		if ((el % 25) == 0)
		printf("\n\t");
	
		printf((el < dectr-1 ? "%d, " : "%d"),dc[el].delen);

}
	printf("\n};\n");
	
/*-----------------------write the file contents array------------*/

for (f = 0;f < fctr; f++)   {
	lcase(name,filename[f]);
	printf("\n\nint %s_file [] = {",name);
	el = 0;
	while ((fel = fileele[f] [el++] ) != 0)
	printf("\n\t%s,",dc[fel-1].dename);
	printf("\n\t0\n};");
}


/*-----------------------write the file list pointer array------------*/



	printf("\n\nint *file_ele [] = {");
	for(f=0;f<fctr; f++)   {
	lcase(name,filename[f]);
	printf("\n\t%s_file,",name);
}

	printf("\n\t0\n};\n");



/*---------write the index arrays------------------------------------*/
for(f=0;f<fctr; f++)   {
	lcase(name,filename[f]);

	for ( x = 0;x < MXINDEX;x++) {

	if(*ndxele [f] [x] == 0)
	break;
	printf("\nint %s_index_%d [] = {",name, x + 1);

    for(cat = 0; cat < MXCAT; cat++)
	if(ndxele [f] [x] [cat])

	printf("\n\t%s,",dc[ndxele [f] [x] [cat]-1].dename);
	printf("\n\t0\n};\n");
}
	printf("\nint *%s_index [] = {",name);
	for(x1 = 0;x1 < x;x1++)
	printf("\n\t%s_index_%d,",name,x1+1);
	printf("\n\t0\n};\n");
}

	printf("\nint **index_ele [] = {");

	for(f=0;f<fctr;f++) {
	lcase(name,filename [f]);
	printf("\n\t%s_index,",name);
}
	printf("\n\t0\n};\n");
}


/*-----write the schema #defines and struct definitions-----------------*/


static void defout()
{
	int f, el ,fel;

	char name[NAMLEN+1];

/*--------data element defines------*/


		for(el = 0;el < dectr;el++)
		printf("\n#define %s %d",dc[el].dename,el+1);
		putchar('\n');
  //--------------write the file #define statements ----------
  for(f=0;f<fctr;f++)
  printf("\n#define %s %d",filename[f],f);
  putchar('\n');

  //---------------write the record structure----------------

  for(f=0;f<fctr;f++)
  {
         lcase(name,filename[f]);
         printf("\nstruct %s {",name);
         el =0;
         while((fel = fileele[f][el++]) != 0) {
         lcase(name,dc[fel - 1].dename);
         printf("\n\tchar %s [%d];",name,dc[fel-1].delen + 1);
         }
         printf("\n};\n");
    }
}

/*---------write the file and data element ascii strings------*/
static void strout()
{
		int el,f;

/*----data element ascii names------*/
		printf("\nchar *denames [] = {");
		for(el = 0;el < dectr; el++)
        printf("\n\t\"%s\",", dc[el].dename);
		printf("\n\t0\n};\n");


/*----data element types---*/
		printf("\nchar eltype [] = \"");
        for(el=0;el<dectr;el++)
		putchar(dc[el].detype);

		printf("\";\n");



/*--data element display masks--------*/
		printf("\nchar *elmask [] = {");
		for (el=0;el<dectr;el++)
		printf((el < dectr - 1 ? "\n\t%s,":"\n\t%s"),dc[el].demask);
		printf("\n};\n");
  //----------write ascii filename strings-------------
         printf("\nchar *dbfiles[] = {");
         for(f= 0;f<fctr;f++)
                printf("\n\t\"%s\",",filename[f]);
         printf("\n\t0\n};\n");
}

/*convert a name to lower case--*/
static void lcase(s1,s2)
char *s1,*s2;

{
		while (*s2) {
		*s1=tolower(*s2);
		s1++;
		s2++;
}
*s1 = '\0';
}





/*-------------errors---------------*/


static void error(n)
int n;
{

		static int erct=0;
		static int erlin=0;
		if(erlin |=lnctr){
           erlin = lnctr;
		fprintf(stderr,"\nLine %d: %s",lnctr,ln);
}

		fprintf(stderr,"\nError   %d: %s",n , ers[n-1]);

		if (erct++ == 5) {
		erct=0;
		fprintf(stderr,"\nContinue? (y/n) ... ");
		if(tolower(getc(stderr)) != 'y')
		depart(1);

}
}

//--------get a line of data from the schema input stream------------

static void get_line()
{
       *ln = '\0';
       while(*ln == '\0' || *ln == REMARK || *ln == '\n')
       {
                 if(fgets(ln,120,stdin) == 0)
                 {
                         error(8);
                         depart(1);
                  }
                  lnctr++;
        }
}

/*------skip over white space----------*/

static void skip_white(s)
char **s;
{
		while(iswhite(**s))
		(*s)++;
}


/*-----------get a word from a line of input------*/

static char *get_word(cp)
char *cp;

{
		int wl = 0,fst = 0;

		skip_white(&cp);
		while(*cp && *cp !='\n'&& *cp!=',' && iswhite(*cp) ==0 ) {

		if (wl == NAMLEN && fst == 0)    {
		error(1);
		fst++;
}
else
		word[wl++] = *cp++;
}

		word[wl] ='\0';
		return cp;
}	

/*--------validate a name--------*/
static void name_val()
{
		char *s = word;
		if(isalpha(*s)) {
		while(isalpha(*s) || isdigit(*s) || *s == '_')  {
		*s = toupper(*s);
		s++;
		}
		if (*s == '\0')
		return;
		}
		error(1);
}


/*--------validate a number--------*/

static void numb_val()
{
		char *s = word;
		do {
		if(isdigit(*s++) == 0) {
		error(2);
		break;
}
		}while(*s);

}



/*---------expect a comma next----*/	

static void expect_comma(cp)
char **cp;
{
		skip_white(cp);
		if(*(*cp)++ !=',')
		error(3);
}


/*---------terminate schema program-------*/
static void depart(n)
int n;
{
		int el;
		for (el = 0;el < dectr;el++)
		free(dc[el].demask);
		exit(n);
}

#if COMPILER == WIZARD


/*----strncmp function to replace non-standard function---*/
int strncmp(s1,s2,n)
char *s1,*s2;
{
		int i;
		while(n--)
		if(i = (*s1++ - *s2++))
		return i;
		return 0;
}

#endif
